import json
import time

from django.shortcuts import render

# Create your views here.
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.exceptions import AuthenticationFailed
# from rest_framework.pagination import PageNumberPagination
from django.db.models import Q

from datetime import datetime, date, timezone
import datetime as datetime_base
import traceback
from copy import deepcopy

from apps.homepage.models import ZtTask, ZtModule, ZtProject, ZtProjectProduct
from .serializers import ZtTaskGanttSerializer, ZtTaskListSerializer, ZtTaskDetailSerializer


import time
from functools import wraps

def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__!r} executed in {(end_time - start_time)*1000:.2f}ms")
        return result
    return wrapper

#判断是否登录
def has_permission(request):

    # session没有token，则未登录
    session_token = request.session.get('token')
    if not session_token:
        return False

    # 如果未传入token，则未登录
    request_token = request.META.get('HTTP_TOKEN')
    if not request_token:
        return False

    #如果都传入，则比对是否一致
    if session_token == request_token:
        return True
    else:
        return False

#查询一个产品是否对应多个项目，如果是需要筛选模块（一个项目对应多个产品，默认这个项目包含所有产品的模块）
# @timing_decorator
def if_select_module(project_id):
    #查询所有项目
    projects = ZtProject.objects.filter(project=0, deleted='0').values('id').distinct()
    projects = [project['id'] for project in projects]

    #查询所有项目与产品的对应表
    product_projectSet_dict = {}  # 产品对应的项目字典
    project_productNum = set()  #项目对应的产品集合

    project_products = ZtProjectProduct.objects.all().values('project', 'product').distinct()

    #计算指定项目对应的产品的项目数量
    for project_product in project_products:
        product = project_product['product']
        project = project_product['project']

        # 项目对应的产品集合
        if project == project_id:
            project_productNum.add(product)

        #产品对应的项目集合
        if product not in product_projectSet_dict.keys():
            product_projectSet_dict[product] = set()
        if project in projects:
            product_projectSet_dict[product].add(project)

    #如果项目没有对应的产品（实际不可能存在）或者没有该项目
    if len(project_productNum) == 0:
        return False
    elif len(project_productNum) == 1:
        product_projectSet = product_projectSet_dict[list(project_productNum)[0]]
        #如果产品只有一个项目，也不需要筛选
        if len(product_projectSet) == 1:
            return False
        else:
            return True
    else:
        #如果一个项目对应多个产品，不筛选模块
        return False

#计算实际的条数
def cal_real(implement_node_real):

    implement_node_real['id'] = 'real-' + implement_node_real['id']
    implement_node_real['real'] = ''

    # 如果有实际开始时间，则实际结束时间和当前对比
    realStarted = implement_node_real['realStarted']
    finishedDate = implement_node_real['finishedDate']
    # 如果不为空，直接取时间，否则取当前时间
    if finishedDate == '':
        if realStarted != '':
            finishedDate = datetime.now().strftime('%Y-%m-%d')
        else:
            finishedDate = ''

    if implement_node_real['status'] in ['已完成', '已取消', '已关闭']:
        implement_node_real['progress'] = 100
    else:
        # 计算进度，实际时间/计划时间，须确保四项均不为空
        if finishedDate != '' and realStarted != '' and implement_node_real['begin'] != '' and implement_node_real[
            'end'] != '':
            # 将字符串转换为datetime对象
            # begin = datetime.strptime(implement_node_real['begin'], "%Y-%m-%d")
            end = datetime.strptime(implement_node_real['end'], "%Y-%m-%d")
            realStarted_date = datetime.strptime(realStarted, "%Y-%m-%d")
            finishedDate_date = datetime.strptime(finishedDate, "%Y-%m-%d")

            if realStarted_date>end:
                progress = ''
            else:
                # 计算两个日期之间的天数差
                plan_days = (end - realStarted_date).days
                plan_days = 1 if plan_days == 0 else plan_days
                real_days = (finishedDate_date - realStarted_date).days
                real_days = 1 if real_days == 0 else real_days
                progress = int(real_days / plan_days * 100)
                if progress == 0 and implement_node_real['status'] != '未开始':
                    progress = 1
            implement_node_real['progress'] = progress
        else:
            implement_node_real['progress'] = ''

    implement_node_real['begin'] = realStarted
    implement_node_real['end'] = finishedDate

    return implement_node_real

#所有开发甘特图数据
class GanttListViewSet(viewsets.ViewSet):

    def initial(self, request, *args, **kwargs):
        super(GanttListViewSet, self).initial(request, *args, **kwargs)
        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            raise AuthenticationFailed({'code': 0, 'msg': '未登录'})

    #甘特图数据
    @timing_decorator
    def gantt(self, request, *args, **kwargs):
        '''
        根据项目id、功能模块id查询对应的需求
        '''
        try:

            #获取参数
            project_id = self.request.query_params.get('project_id', None)
            execution_id = self.request.query_params.get('execution_id', None)
            module_select_id = self.request.query_params.get('module_id', None)

            #如果project_id没有传
            if not project_id:
                return Response({'code': 0, 'msg': '没有携带项目id'}, status=status.HTTP_400_BAD_REQUEST)

            # if module_id:
            #     #先获取包含该id的所有模块(父子节点)
            #     modules = ZtModule.objects.filter(path__icontains=module_id, deleted='0')
            # else:
            #获取该项目所有模块
            root_ids = set()
            # 查询项目及对应的迭代（迭代也可以建模块）
            query_project = Q(id=project_id) | Q(project=project_id)
            query_project = Q(deleted='0') & query_project
            project_excutions = ZtProject.objects.filter(query_project).values('id').distinct()
            for project_excution in project_excutions:
                root_ids.add(project_excution['id'])

            # 查询项目对应的产品
            project_products = ZtProjectProduct.objects.filter(project__in=root_ids).values('product').distinct()
            for project_product in project_products:
                root_ids.add(project_product['product'])

            # 获取该项目所有模块
            query_module = Q()
            # query_module &= Q(root__projectProduct_product__project_id=project_id)  # 根据项目id查询模块
            # query_module &= Q(root__projectProduct_product__project__deleted='0')  # 项目不能被删除
            # 使用括号来确保“或”条件与之前的“与”条件组合
            query_module &= Q(root__in=root_ids)
            query_module &= Q(deleted='0')  # 模块不能被删除

            modules = ZtModule.objects.filter(query_module).values('id', 'name', 'path').distinct()

            # 处理模块将,151,152,改为,738,151,152,
            # 获取所有的位尾节点模块
            model_path_end = {}
            for module in modules:
                path = module['path'].strip(',').split(',')
                if len(path) > 1:
                    model_path_end[path[-1]] = {'id': module['id'], 'path': module['path']}

            # 处理首节点对应尾巴节点的情况
            for i, module in enumerate(modules):
                path = module['path'].strip(',').split(',')
                if len(path) > 1 and path[0] in model_path_end.keys():
                    pre_path = model_path_end[path[0]]
                    path = pre_path['path'] + ''.join(path[1:]) + ','
                    module['path'] = path

            # 排序，将父模块放到前面
            modules = sorted(modules, key=lambda x: len(x['path'].split(',')))

            #整理模块树结构
            module_tree, path_map = self.module_to_tree(modules)
            # print(path_map)

            # 搜索条件
            # 初始化一个空的查询集
            query = Q()
            query &= Q(project__id=project_id)  # 根据项目id查询需求
            query &= Q(project__deleted='0')  # 项目不能被删除
            query &= Q(deleted='0')  #任务不能被删除
            query &= Q(type='implement')  # 任务类型为实施
            # query &= Q(module__id__in=path_map.keys())  # 对应执行的任务

            #如果有执行id
            if execution_id:
                query &= Q(execution=execution_id)  # 对应执行的任务

            implements = ZtTask.objects.filter(query).exclude(status='cancel').select_related('assignedTo','finishedBy', 'module')\
                .prefetch_related('taskteam_task','taskteam_task__account').distinct()

            # _status = '未开始'  #默认为未开始
            # 梳理父子结构
            implement_dict = {}
            name_map = {}

            # 转换关系
            status_map = {
                'wait': '未开始',
                'doing': '进行中',
                'done': '已完成',
                'pause': '已暂停',
                'cancel': '已取消',
                'closed': '已关闭',
            }

            for implement in implements:

                # 如果是多人任务则拼接，否则使用指派人
                team_list = implement.taskteam_task.all()  # 获取该任务所有人
                if team_list:
                    assignedTo_account = set()
                    assignedTo_realname = set()
                    for person in team_list:

                        name_map[person.account.account] = person.account.realname

                        assignedTo_account.add(person.account.account)
                        assignedTo_realname.add(person.account.realname)

                    assignedTo_account = ';'.join(assignedTo_account)
                    assignedTo_realname = ';'.join(assignedTo_realname)
                else:
                    # 获取用户名和真实姓名
                    assignedTo = implement.assignedTo

                    if assignedTo:
                        assignedTo_account = assignedTo.account
                        assignedTo_realname = assignedTo.realname
                    else:
                        assignedTo_account = ''
                        assignedTo_realname = ''

                    # 如果有完成人，并且状态为关闭或完成，则用完成人替代
                    if implement.finishedBy and implement.status in ['closed', 'done']:
                        assignedTo_account = implement.finishedBy.account
                        assignedTo_realname = implement.finishedBy.realname

                    name_map[assignedTo_account] = assignedTo_realname

                # 修改状态
                implement.status = status_map[implement.status]

                if implement.deadline is not None:

                    # 判断给定的日期时间是否已经超期（即是否在当前日期时间之前）
                    is_overdue = implement.deadline < datetime.now(timezone.utc).date()
                    if is_overdue and implement.status not in ['已完成', '已关闭']:
                        implement.status = '超期告警'

                # #如果日期为空转为''，否则截断到日
                # for key in ['estStarted', 'deadline', 'realStarted', 'finishedDate']:
                #     if implement[key] is None:
                #         implement[key] = ''
                #     else:
                #         implement[key] = implement[key].strftime('%Y-%m-%d')

                # 模块id
                if implement.module:
                    if implement.module.id in path_map.keys():
                        module_id = implement.module.id
                        module_path = path_map[module_id]
                    else:
                        module_id = 0
                        module_path = ',0,'
                else:
                    module_id = 0
                    module_path = ',0,'

                implement_node = {
                    "id": 'implement-{}'.format(implement.id),
                    "parent": implement.parent,
                    "module": module_id,
                    'name': implement.name,
                    'status': implement.status,
                    'begin': '' if implement.estStarted is None else implement.estStarted.strftime('%Y-%m-%d'),
                    'end': '' if implement.deadline is None else implement.deadline.strftime('%Y-%m-%d'),
                    'realStarted': '' if implement.realStarted is None else implement.realStarted.strftime('%Y-%m-%d'),
                    'finishedDate': '' if implement.finishedDate is None else implement.finishedDate.strftime('%Y-%m-%d'),
                    'assignedTo': assignedTo_realname,
                    'assignedTo_account': assignedTo_account,
                    'module_path': module_path,
                    'type': "implement",
                    'child': {},
                }

                # implement_dict[implement.id] = implement_node
                # 如果parent不为0或-1，则需要将其加入父节点中
                # 如果parent不为0或-1，则需要将其加入父节点中，统计父需求的所有指派人
                if implement.parent == 0 or implement.parent == -1:
                    implement_dict[implement.id] = implement_node
                    if implement.parent == 0:
                        implement_node_real = deepcopy(implement_node)
                        implement_node_real = cal_real(implement_node_real)

                        implement_dict[f'real-{implement.id}'] = implement_node_real
                else:

                    if implement.parent in implement_dict.keys():
                        if assignedTo_account != '':
                            parent_account = set(implement_dict[implement.parent]['assignedTo_account'].split(';'))
                            child_account = set(implement_node['assignedTo_account'].split(';'))
                            implement_dict[implement.parent]['assignedTo_account']=';'.join(parent_account|child_account)
                        implement_dict[implement.parent]['child'][implement.id] = implement_node
                        implement_node_real = deepcopy(implement_node)
                        implement_node_real = cal_real(implement_node_real)
                        implement_dict[implement.parent]['child'][f'real-{implement.id}'] = implement_node_real
                    else:
                        # 如果没有父节点（实际没有这种情况）
                        implement_dict[implement.id] = implement_node

            #将需求挂载到模块树上
            module_tree = self.mount_stories_to_modules(module_tree, implement_dict, path_map)

            ##没子任务但是标注为-1，也需要两条
            implement_node_reals = []

            # 将模块为0的放到根节点上
            for implement_id, implement in implement_dict.items():
                if implement['module'] == 0:
                    module_tree['implement-{}'.format(implement_id)] = implement

                if len(implement['child']) == 0 and implement['parent'] == -1:
                    implement_node_real = deepcopy(implement)
                    implement_node_reals.append([implement_id,implement_node_real])

            # 没子任务但是标注为-1，也需要两条
            for implement_id, implement_node_real in implement_node_reals:
                implement_node_real = cal_real(implement_node_real)
                module_tree['real-implement-{}'.format(implement_id)] = implement_node_real

            # 更新父节点和模块的状态
            module_tree = self.update_tree_status(module_tree, name_map=name_map)

            # 如果选了模块，则根据_type和_id条件进行筛选
            if module_select_id:
                filter_data = {'type': 'module', 'id': module_select_id}
                module_tree = self.get_modules_by_select(module_tree, filter_data)

            # 转为列表形式
            module_tree_list = list(module_tree.values())

            # 如果该项目对应的产品有多个项目，则需要筛选模块
            # if if_select_module(int(project_id)):
                # 筛选有需求的模块
            module_tree_list = self.remove_empty_child_modules(module_tree_list)

            # return Response({'code': 1, 'msg': '获取成功', 'data': list(module_tree.values())})
            return Response({'code': 1, 'msg': '获取成功', 'data': module_tree_list})
        except:
            print(traceback.format_exc())
            return Response({'code': 0, 'msg': '获取失败'},status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    # 列表数据
    @timing_decorator
    def list(self, request, *args, **kwargs):
        '''
        根据项目id、功能模块id、需求id查询对应的需求,还可根据名称、状态、指派人进行搜索
        '''
        try:
            # 获取项目等参数
            project_id = self.request.query_params.get('project_id', None)
            _id = self.request.query_params.get('id', None)
            _type = self.request.query_params.get('type', None)
            module_path = self.request.query_params.get('module_path', None)

            # 如果project_id, _id, _type, path没有传
            if not (project_id and _id and _type and module_path):
                return Response({'code': 0, 'msg': '缺少参数'}, status=status.HTTP_400_BAD_REQUEST)

            #模块根节点
            root_path = module_path.strip(',').split(',')[0]

            #获取搜索参数
            filter_name = self.request.query_params.get('name', None)
            filter_status = self.request.query_params.get('status', None)
            filter_assignedTo = self.request.query_params.get('assignedTo', None)

            # if module_id:
            #     #先获取包含该id的所有模块(父子节点)
            #     modules = ZtModule.objects.filter(path__icontains=module_id, deleted='0')
            # else:
            # 获取该项目所有模块
            root_ids = set()
            # 查询项目及对应的迭代（迭代也可以建模块）
            query_project = Q(id=project_id) | Q(project=project_id)
            query_project = Q(deleted='0') & query_project
            project_excutions = ZtProject.objects.filter(query_project).values('id').distinct()
            for project_excution in project_excutions:
                root_ids.add(project_excution['id'])

            # 查询项目对应的产品
            project_products = ZtProjectProduct.objects.filter(project__in=root_ids).values('product').distinct()
            for project_product in project_products:
                root_ids.add(project_product['product'])

            # 获取该项目所有模块
            query_module = Q()
            # query_module &= Q(root__projectProduct_product__project_id=project_id)  # 根据项目id查询模块
            # query_module &= Q(root__projectProduct_product__project__deleted='0')  # 项目不能被删除
            # 使用括号来确保“或”条件与之前的“与”条件组合
            query_module &= Q(root__in=root_ids)
            query_module &= Q(deleted='0')  # 模块不能被删除

            modules = ZtModule.objects.filter(query_module).values('id', 'name', 'path').distinct()

            # 处理模块将,151,152,改为,738,151,152,
            # 获取所有的位尾节点模块
            model_path_end = {}
            for module in modules:
                path = module['path'].strip(',').split(',')
                if len(path) > 1:
                    model_path_end[path[-1]] = {'id': module['id'], 'path': module['path']}

            # 处理首节点对应尾巴节点的情况
            for i, module in enumerate(modules):
                path = module['path'].strip(',').split(',')
                if len(path) > 1 and path[0] in model_path_end.keys():
                    pre_path = model_path_end[path[0]]
                    path = pre_path['path'] + ''.join(path[1:]) + ','
                    module['path'] = path

            # 排序，将父模块放到前面
            modules = sorted(modules, key=lambda x: len(x['path'].split(',')))

            # 整理模块树结构
            module_tree, path_map = self.module_to_tree(modules, mode='list')
            # print(path_map)

            # 搜索条件
            # 初始化一个空的查询集
            query = Q()
            query &= Q(project__id=project_id)  # 根据项目id查询开发任务
            query &= Q(project__deleted='0')  # 项目不能被删除
            query &= Q(deleted='0')  # 需求不能被删除
            query &= Q(type='implement')  # 任务类型为实施
            # query &= Q(module__id__in=path_map.keys())  # 对应模块的需求

            implements = ZtTask.objects.filter(query).exclude(status='cancel').select_related('assignedTo','finishedBy', 'module') \
                .prefetch_related('taskteam_task', 'taskteam_task__account').distinct()

            # _status = '未开始'  #默认为未开始
            # 梳理父子结构
            implement_dict = {}
            name_map = {}
            team = set()

            # 转换关系
            status_map = {
                'wait': '未开始',
                'doing': '进行中',
                'done': '已完成',
                'pause': '已暂停',
                'cancel': '已取消',
                'closed': '已关闭',
            }

            #类型关系
            category_map = {
                'design': '设计',
                'devel': '开发',
                'request': '需求',
                'test': '测试',
                'study': '研究',
                'discuss': '讨论',
                'ui': '界面',
                'affair': '事务',
                'misc': '其他',
            }

            for implement in implements:

                # 如果是多人任务则拼接，否则使用指派人
                team_list = implement.taskteam_task.all()  # 获取任务团队

                if team_list:
                    assignedTo_account = set()
                    assignedTo_realname = set()
                    for person in team_list:
                        assignedTo_account.add(person.account.account)
                        assignedTo_realname.add(person.account.realname)
                        name_map[person.account.account] = person.account.realname
                        team.add(person.account.account)

                    assignedTo_account = ';'.join(assignedTo_account)
                    assignedTo_realname = ';'.join(assignedTo_realname)
                else:
                    # 获取用户名和真实姓名
                    assignedTo = implement.assignedTo

                    if assignedTo:
                        assignedTo_account = assignedTo.account
                        assignedTo_realname = assignedTo.realname
                        team.add(assignedTo_account)
                    else:
                        assignedTo_account = ''
                        assignedTo_realname = ''

                    # 如果有完成人，并且状态为关闭或完成，则用完成人替代
                    if implement.finishedBy and implement.status in ['closed', 'done']:
                        assignedTo_account = implement.finishedBy.account
                        assignedTo_realname = implement.finishedBy.realname

                    name_map[assignedTo_account] = assignedTo_realname

                # 修改状态
                implement.status = status_map[implement.status]

                if implement.deadline is not None:

                    # 判断给定的日期时间是否已经超期（即是否在当前日期时间之前）
                    is_overdue = implement.deadline < datetime.now(timezone.utc).date()
                    if is_overdue and implement.status not in ['已完成', '已关闭']:
                        implement.status = '超期告警'

                # #如果日期为空转为''，否则截断到日
                # for key in ['estStarted', 'deadline', 'realStarted', 'finishedDate']:
                #     if implement[key] is None:
                #         implement[key] = ''
                #     else:
                #         implement[key] = implement[key].strftime('%Y-%m-%d')
                # 模块id
                if implement.module:
                    if implement.module.id in path_map.keys():
                        module_id = implement.module.id
                    else:
                        module_id = 0
                else:
                    module_id = 0

                implement_node = {
                    "id": 'implement-{}'.format(implement.id),
                    "module": module_id,
                    'name': implement.name,
                    'status': implement.status,
                    'category': category_map[implement.type],
                    'pri': implement.pri,
                    'begin': '' if implement.estStarted is None else implement.estStarted.strftime('%Y-%m-%d'),
                    'end': '' if implement.deadline is None else implement.deadline.strftime('%Y-%m-%d'),
                    'realStarted': '' if implement.realStarted is None else implement.realStarted.strftime('%Y-%m-%d'),
                    'finishedDate': '' if implement.finishedDate is None else implement.finishedDate.strftime('%Y-%m-%d'),
                    'assignedTo': assignedTo_realname,
                    'assignedTo_account': assignedTo_account,
                    'type': "implement",
                    'child': {},
                }

                # implement_dict[implement.id] = implement_node
                # 如果parent不为0或-1，则需要将其加入父节点中
                # 如果parent不为0或-1，则需要将其加入父节点中，统计父需求的所有指派人
                if implement.parent == 0 or implement.parent == -1:
                    implement_dict[implement.id] = implement_node
                else:
                    if implement.parent in implement_dict.keys():
                        if assignedTo_account != '':
                            parent_account = set(implement_dict[implement.parent]['assignedTo_account'].split(';'))
                            child_account = set(implement_node['assignedTo_account'].split(';'))
                            implement_dict[implement.parent]['assignedTo_account']=';'.join(parent_account|child_account)
                        implement_dict[implement.parent]['child'][implement.id] = implement_node
                    else:
                        # 如果没有父节点（实际没有这种情况）
                        implement_dict[implement.id] = implement_node

            # 将需求挂载到模块树上
            module_tree = self.mount_stories_to_modules(module_tree, implement_dict, path_map)

            # 更新父节点和模块的状态
            module_tree = self.update_tree_status(module_tree, name_map=name_map)

            #根据_type和_id条件进行筛选
            _id = _id.replace('real-','')
            filter_data = {'type': _type, 'id': _id}
            module_tree_select = self.get_modules_by_select(module_tree, filter_data)

            #如果有搜索条件
            if filter_assignedTo or filter_status or filter_name:
                filter_data = {'name': filter_name, 'status': filter_status, 'assignedTo': filter_assignedTo}
                module_tree_select = self.get_modules_by_select(module_tree_select, filter_data)
            #     return Response({'code': 1, 'msg': '获取成功', 'data': list(module_tree_select.values())})
            # else:
            return Response({'code': 1, 'msg': '获取成功', 'data': {'teamCount': len(team), 'list':list(module_tree_select.values())}})
        except:
            print(traceback.format_exc())
            return Response({'code': 0, 'msg': '获取失败'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    # 将路径整理成树结构的函数
    def module_to_tree(self, modules, mode='gantt'):

        # 初始化树结构
        tree = {}

        #模块_路径对应表
        path_map = {}

        # 遍历路径列表，构建树结构
        for module in modules:
            path_map[module['id']] = module['path']
            # 移除路径两端的逗号，并分割成步骤列表
            steps = module['path'].strip(',').split(',')

            # 递归地构建或更新树结构
            current_level = tree
            for step in steps:
                if step:  # 确保步骤不为空
                    if step not in current_level:
                        if mode == 'gantt':
                            current_level[step] = {'id':'module-{}'.format(module['id']),
                                                   'name':module['name'],
                                                   'status':'未开始',
                                                   'begin':'',
                                                   'end':'',
                                                   'assignedTo': '',
                                                   'assignedTo_account': '',
                                                   'module_path': module['path'],
                                                   'type': "module",
                                                   'child': {}}
                        elif mode == 'list':
                            current_level[step] = {'id':'module-{}'.format(module['id']),
                                                   'name': module['name'],
                                                   'status': '未开始',
                                                   'pri': "",
                                                   'begin': '',
                                                   'end': '',
                                                   'realStarted': '',
                                                   'finishedDate': '',
                                                   'assignedTo': '',
                                                   'assignedTo_account': '',
                                                   'type': "module",
                                                   'child': {}}

                    current_level = current_level[step]['child']
        return tree, path_map

    #将需求挂载到模块下面
    def mount_stories_to_modules(self, module_tree, story_tree, path_map):

        #根据给定的路径在模块树中递归查找模块。
        def find_module_by_path(modules, path):
            if not path or not modules:
                return None
            current_id = path.pop(0)
            if current_id in modules:
                if not path:
                    return modules[current_id]
                else:
                    return find_module_by_path(modules[current_id]['child'], path)
            return None

        for story_id, story in story_tree.items():

            # 如果模型为0，即未挂载模块，则不挂载到模块下面
            if story['module'] == 0:
                continue

            #路径列表
            module_path = path_map[story['module']].strip(',').split(',')

            target_module = find_module_by_path(module_tree, module_path)
            if target_module:
                # if 'child' not in target_module:
                #     target_module['child'] = {}
                if 'child' in target_module:
                    target_module['child']['implement'+str(story_id)] = story
        return module_tree

    #更新树的状态及日期
    def update_tree_status(self, tree, name_map={}):

        #更新状态和日期
        def update_parent_status(node):
            # 获取子节点字典
            children = node.get('child', {})

            # 如果没有子节点，则不更新状态
            if not children:

                # 将字段转为列表
                node['child'] = list(node['child'].values())
                return

            # 初始化状态集合
            status_set = set()
            begin_list = []
            end_list = []
            assignedTo_set = set()

            #子任务计算模块状态时修改状态
            modify_status = {
                '未开始': '未开始',
                '进行中': '进行中',
                '已完成': '已完成',
                '已暂停': '进行中',
                '已取消': '未开始',
                '已关闭': '已完成',
                '超期告警': '超期告警'
            }

            # 遍历所有子节点
            for child_node in children.values():
                # 递归更新子节点的状态
                update_parent_status(child_node)
                #子节点状态集合
                status_set.add(modify_status[child_node['status']])

                #日期
                if child_node['begin'] != '':
                    begin_list.append(child_node['begin'])
                if child_node['end'] != '':
                    end_list.append(child_node['end'])

                #指派人
                if 'assignedTo_account' in child_node.keys():
                    if child_node['assignedTo_account']:
                        #如果是集合，则转为字符串
                        # if isinstance(child_node['assignedTo_account'], set):
                        #     child_node['assignedTo_account'] = ';'.join(child_node['assignedTo_account'])

                        if ';' not in child_node['assignedTo_account']:
                            assignedTo_set.add(child_node['assignedTo_account'])
                            # child_node['assignedTo'] = name_map[child_node['assignedTo_account']]
                        else:
                            assignedTo_list = child_node['assignedTo_account'].split(';')
                            assignedTo_set |= set(assignedTo_list)
                            # child_node['assignedTo'] = ';'.join([name_map[i] for i in assignedTo_list])

                    del child_node['assignedTo_account']

            #只有为模块才需要更新日期和指派人
            if node['type'] == 'module' and begin_list:
                node['begin'] = min(begin_list)
            if node['type'] == 'module' and end_list:
                node['end'] = max(end_list)

            #将用户名转为姓名然后删除用户名
            if node['type'] == 'module' and assignedTo_set:
                node['assignedTo_account'] = ';'.join(assignedTo_set)
                if ';' not in node['assignedTo_account']:
                    node['assignedTo'] = name_map[node['assignedTo_account']]
                else:
                    assignedTo_list = node['assignedTo_account'].split(';')
                    node['assignedTo'] = ';'.join([name_map[i] for i in assignedTo_list])

            # 如果全是未开始，则为未开始
            if status_set == set(['未开始']):
                node['status'] = '未开始'
            # 如果只有完成和超期或者全为超期，则为超期
            elif status_set == set(['超期告警']):
                node['status'] = '超期告警'
            # 如果全为完成，则为完成
            elif status_set == set(['已完成']):
                node['status'] = '已完成'
            # 其余均为进行中
            else:
                node['status'] = '进行中'

            #将字段转为列表
            node['child'] = list(node['child'].values())

        #更新状态和日期
        for _, module in tree.items():
            update_parent_status(module)

        #删除根节点的assignedTo_account
        for key in tree.keys():
            del tree[key]['assignedTo_account']

        return tree

    # 根据id获取指定id的父节点和子节点
    # def get_module_with_parents(self, tree, module_id):
    #     # 递归函数，用于获取指定module_id的节点及其所有父节点
    #     def retrieve_module(node, module_id):
    #         # 如果当前节点是目标模块，则返回当前节点
    #         # if node['id'] in module_id:
    #         if node['status'] == '已完成':
    #             return node
    #
    #         # 遍历子节点列表
    #         for child_node in node.get('child', []):
    #             # 递归搜索子节点
    #             result = retrieve_module(child_node, module_id)
    #             # 如果在子节点中找到目标模块，则将子节点添加到当前节点的child中，并返回当前节点
    #             if result is not None:
    #                 node['child'] = [result]
    #                 return node
    #         # 如果当前节点及其子节点中都没有找到目标模块，则返回None
    #         return None
    #
    #     # 遍历整棵树，寻找目标模块及其父节点
    #     for mod_node in tree.values():
    #         module_data = retrieve_module(mod_node, module_id)
    #         if module_data is not None:
    #             return module_data
    #     return None  # 如果没有找到对应的模块，则返回None

    #根据条件进行筛选
    def get_modules_by_select(self, tree, filter_data):
        # 递归函数，用于遍历树并找到所有匹配status和name的模块及其所有子节点
        def traverse_and_filter(node):

            #默认为True，不匹配直接返回
            judge = True
            for key, value in filter_data.items():
                judge = judge and (value is None or value in str(node[key]))
                if not judge:
                    break

            # 如果当前节点匹配status和name，则保留该节点及其所有子节点
            if judge:
                # 直接返回当前节点及其所有子节点
                return node.copy()

            # 如果当前节点不匹配，继续遍历子节点
            filtered_children = []
            for child_node in node.get('child', []):
                child_result = traverse_and_filter(child_node)
                if child_result:
                    filtered_children.append(child_result)

            # 如果子节点中有匹配的，则保留当前节点，包含所有子节点
            if filtered_children:
                filtered_node = node.copy()
                filtered_node['child'] = filtered_children
                return filtered_node

            # 如果没有匹配的子节点，返回None
            return None

        # 遍历整棵树，应用筛选条件
        filtered_tree = {}
        for mod_id, mod_node in tree.items():
            result = traverse_and_filter(mod_node)
            if result:
                filtered_tree[mod_id] = result

        return filtered_tree

    # 删除模块的child为空的情况
    # @timing_decorator
    def remove_empty_child_modules_node(self, data):
        # If the data itself is empty, return None
        if not data:
            return None
        # with open('1.json','w') as f:
        #     json.dump(data,f,ensure_ascii=False)
        # Check if the current node is a module
        if data['type'] == 'module':
            # Recursively remove empty modules from children
            data['child'] = [self.remove_empty_child_modules_node(child) for child in data['child']]
            # Filter out None values from the list after recursion
            data['child'] = [child for child in data['child'] if child is not None]
            # If after removing children, the module has no children left, remove it
            if not data['child']:
                return None
        return data

    def remove_empty_child_modules(self,data):
        # Process each item in the list
        return [self.remove_empty_child_modules_node(item) for item in data if
                self.remove_empty_child_modules_node(item) is not None]

#开发详细数据
class DetailViewSet(viewsets.ViewSet):
    # queryset = ZtTask.objects
    # serializer_class = ZtTaskDetailSerializer

    def initial(self, request, *args, **kwargs):
        super(DetailViewSet, self).initial(request, *args, **kwargs)
        # 检查用户是否登录
        if not has_permission(request):
            # 如果用户未登录，抛出 AuthenticationFailed 异常
            raise AuthenticationFailed({'code': 0, 'msg': '未登录'})

    #格式化数据
    @timing_decorator
    def list(self, request, *args, **kwargs):
        '''
        根据项目id、功能模块id查询对应的开发任务
        '''
        try:
            #获取参数
            implement_id = self.request.query_params.get('implement_id', None)


            #如果project_id没有传
            if not implement_id:
                return Response({'code': 0, 'msg': '没有携带开发id'}, status=status.HTTP_400_BAD_REQUEST)

            implement_id = implement_id.split('-')[-1]

            implement = ZtTask.objects.filter(deleted='0', id=implement_id,type='implement').exclude(status='cancel').select_related('assignedTo','finishedBy', 'execution', 'module', 'story')\
                                                .prefetch_related('taskteam_task', 'taskteam_task__account').distinct().first()

            # 未查询到
            if not implement:
                return Response({'code': 0, 'msg': '该实施不存在'})

            # 转换关系
            status_map = {
                'wait': '未开始',
                'doing': '进行中',
                'done': '已完成',
                'pause': '已暂停',
                'cancel': '已取消',
                'closed': '已关闭',
            }

            # 类型关系
            category_map = {
                'design': '设计',
                'devel': '开发',
                'request': '需求',
                'test': '测试',
                'study': '研究',
                'discuss': '讨论',
                'ui': '界面',
                'affair': '事务',
                'misc': '其他',
            }

            #团队
            team = set()

            # 如果是多人任务则拼接，否则使用指派人
            team_list = implement.taskteam_task.all()  # 获取最后一个版本的描述
            if team_list:
                assignedTo_account = set()
                assignedTo_realname = set()
                for person in team_list:
                    assignedTo_account.add(person.account.account)
                    assignedTo_realname.add(person.account.realname)
                    team.add(person.account.account)

                assignedTo_account = ';'.join(assignedTo_account)
                assignedTo_realname = ';'.join(assignedTo_realname)
            else:
                # 获取用户名和真实姓名
                assignedTo = implement.assignedTo

                if assignedTo:
                    assignedTo_account = assignedTo.account
                    assignedTo_realname = assignedTo.realname
                    team.add(assignedTo_account)
                else:
                    assignedTo_account = ''
                    assignedTo_realname = ''

                # 如果有完成人，并且状态为关闭或完成，则用完成人替代
                if implement.finishedBy and implement.status in ['closed', 'done']:
                    assignedTo_account = implement.finishedBy.account
                    assignedTo_realname = implement.finishedBy.realname

            #获取执行版本
            execution = implement.execution
            if execution:
                execution_name = '#{} {}'.format(execution.id, execution.name)
            else:
                execution_name = ''

            # 获取模块名称
            module = implement.module
            if module:
                module_name = '#{} {}'.format(module.id, module.name)
            else:
                module_name = ''

            # 获取需求名称
            story = implement.story
            if story:
                story_name = '#{} {}'.format(story.id, story.title)
            else:
                story_name = ''

            # 修改状态
            implement.status = status_map[implement.status]

            if implement.deadline is not None:

                # 判断给定的日期时间是否已经超期（即是否在当前日期时间之前）
                is_overdue = implement.deadline < datetime.now(timezone.utc).date()
                if is_overdue and implement.status not in ['已完成', '已关闭']:
                    implement.status = '超期告警'

            implement = {
                'id': implement.id,
                'name': implement.name,
                'status': implement.status,
                'category': category_map[implement.type],
                'pri': implement.pri,
                'begin': '' if implement.estStarted is None else implement.estStarted.strftime('%Y-%m-%d'),
                'end': '' if implement.deadline is None else implement.deadline.strftime('%Y-%m-%d'),
                'realStarted': '' if implement.realStarted is None else implement.realStarted.strftime('%Y-%m-%d'),
                'finishedDate': '' if implement.finishedDate is None else implement.finishedDate.strftime('%Y-%m-%d'),
                'assignedTo': assignedTo_realname,
                'module': module_name,
                'story': story_name,
                'execution': execution_name,
                'desc': implement.desc
            }

            return Response({'code': 1, 'msg': '获取成功', 'data': {'teamCount':len(team), 'detail':implement}})
        except:
            print(traceback.format_exc())
            return Response({'code': 0, 'msg': '获取失败'},status=status.HTTP_500_INTERNAL_SERVER_ERROR)